package navigation {
	import org.splink.deepsplink.navigation.INavigation;
	import org.splink.deepsplink.navigation.command.INavigationCommand;
	import org.splink.library.tree.INode;
	import org.splink.library.tree.Tree;
	import org.splink.library.utils.ArrayUtils;

	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.MouseEvent;

	/**
	 * @author Max Kugland
	 */
	public class TreeNavigation implements INavigation {
		private var _tree : Tree;
		private var _btnAr : Array;
		private var _display : Sprite;
		private var _clickCommand : INavigationCommand;
		private var _excludeIds : Array;

		public function TreeNavigation(xml : XML, clickCommand : INavigationCommand, excludeIds : Array) {
			_display = new Sprite();
			_clickCommand = clickCommand;
			_excludeIds = excludeIds;

			_tree = Tree.fromXml(new Tree(), xml, StatefulNode);
			Tree.visit(_tree.root, function(n : INode) : void {
				n.data = n.id;
			});
			
			renderTree();
		}

		/**
		 * Initially draws the tree and redraws the tree everytime a node has been clicked.
		 */
		private function renderTree() : void {
			_btnAr = new Array();
			var ar : Array = _tree.toArray();
			var lastY : int = 0;
			for each (var node : StatefulNode in ar) {
				if(node.open && node != _tree.root && !ArrayUtils.contains(_excludeIds, node.id)) {
					var btn : Btn = new Btn(node.id, node.data);
					btn.addEventListener(MouseEvent.CLICK, onRelease);
					
					btn.display.x = node.layer * btn.display.height;
					btn.display.y = lastY;
					_display.addChild(btn.display);
					
					lastY = btn.display.y + btn.display.height;

					_btnAr.push(btn);
				}
			}
		}

		/**
		 * Gets invoked when a node has been clicked and starts processing the changes to the tree
		 */
		private function onRelease(e : MouseEvent) : void {
			_clickCommand.gotoPage((e.target as Btn).id);
		}

		/**
		 * @inheritDoc
		 * Select a node within the tree
		 */
		public function select(id : String, pageParams : Array = null, changed : Boolean = true) : void {
			var node : StatefulNode = Tree.getNodeById(id, _tree.root) as StatefulNode;
			if(node == null) {
				return;
			}
			
			if(!node.hasOpenChilds()) {
				for each (var child : StatefulNode in node.children) {
					child.open = true;
					openClosedChildren(child);
				}
				
				node.open = true;
				openParents(node);
			} else {
				closeChildren(node);
			}
			
			finishTree();
			renderTree();
			
			markButton(node.id);
		}

		/**
		 * Clears the displaylist
		 */
		private function finishTree() : void {
			while(_display.numChildren > 0) {
				_display.removeChildAt(0);
			}
		}

		/**
		 * Marks the button, which belongs to the given id and unmarks all other buttons
		 * 
		 * @param id the id of the button to mark
		 */
		private function markButton(id : String) : void {
			for each (var btn : Btn in _btnAr) {
				if(btn.id == id) {
					btn.mark();
				} else {
					btn.unmark();
				}
			}
		}

		/**
		 * Opens all nodes beneath the given node if they have been recently closed
		 * 
		 * @param node the node from which to start
		 */
		private function openClosedChildren(node : StatefulNode) : void {
			for each (var child : StatefulNode in node.children) {
				if(child.closed) {
					child.closed = true;
					openClosedChildren(child);
				}
			}
		}

		/**
		 * Closes all children of the given node and sets a flag on them if they have been closed
		 * recently, to enable the reopening of a complete branch
		 * 
		 * @param node the node from which to start
		 */
		private function closeChildren(node : StatefulNode) : void {
			for each (var child : StatefulNode in node.children) {
				if(child.open) {
					child.closed = true;
				} else {
					child.closed = false;
				}
				
				child.open = false;
				closeChildren(child);
			}
		}

		/**
		 * Openens all nodes beneath the given node.
		 * 
		 * @param node the node whose parents will be opened 
		 */
		private function openParents(node : StatefulNode) : void {
			var parent : StatefulNode = node.parent as StatefulNode;
			if(parent) {
				parent.open = true;
				
				for each (var child : StatefulNode in parent.children) {
					child.open = true;
				}
				
				openParents(parent);
			}
			else node.open = true;
		}

		public function finalize() : void {
			finishTree();
			_tree.finalize();
		}
		
		public function get display() : DisplayObject {
			return _display;
		}
	}
}
